/*
 * Project: Sudoku Explainer
 * Copyright (C) 2006-2007 Nicolas Juillerat
 * 
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver.core;

import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;

import com.onegravity.sudoku.solver.HintType;
import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.data.Grid;
import com.onegravity.sudoku.solver.data.ImmutableCell;

/**
 * Abstract class for hints that do not directly allow the placement
 * of a value in a cell, but allow the removal of one or more potential
 * values of one or more cells of the sudoku grid.
 */
public abstract class IndirectHint extends Hint {

	private static final long serialVersionUID = -1396467839990195945L;
	
    private final Map<Cell,BitSet> removablePotentials;
    private final Map<ImmutableCell,BitSet> toBeRemovedApplied;

    /**
     * Create a new indirect hint.
     * @param rule the rule that discovered the hint
     * @param removablePotentials the potential values that can be removed
     * in cells of the sudoku grid by applying this hint.
     */
    protected IndirectHint(Map<Cell, BitSet> removablePotentials) {
        this.removablePotentials = removablePotentials;
		this.toBeRemovedApplied = new HashMap<ImmutableCell, BitSet>();
    }

	@Override
	public Cell getCell(HintType hintType) {
		return null;
	}
    
	@Override
	public int getValue() {
		return 0;
	}

    /**
     * Get the potential values that can be removed from cells of the sudoku
     * grid by applying this hint. The keys are cells of the grid, and the
     * values are the bit set of values that can be removed from the corresponding
     * cell. Note that the bitsets can only contain values ranging between 1 and 9.
     * @return the potential values that can be removed from cells of the sudoku
     * grid by applying this hint
     */
    public Map<Cell,BitSet> getRemovablePotentials() {
        return this.removablePotentials;
    }

	@Override
	public Map<ImmutableCell, BitSet> getRemovedPotentials(HintType hintType) {
        return toBeRemovedApplied;
    }

    /**
     * Test is this hint is worth, that is, if it really allows one to remove
     * at least one potential value from a cell of the grid.
     * <p>
     * This method is used, because it is frequent that a solving pattern is
     * discovered, but does not allow any progress in the current grid. But this
     * fact is hard to discover before the pattern itself.
     * @return whether this hint allows some progress in the solving process
     */
    public boolean isWorth() {
        return !removablePotentials.isEmpty();
    }

    /**
     * Apply this hint on the grid (passed to the constructor).
     */
    @Override
    public Grid apply() {
    	Grid theGrid = null;
        for (Cell cell : removablePotentials.keySet()) {
        	if (theGrid==null) theGrid = cell.getGrid(); 
    		ImmutableCell immutableCell = new ImmutableCell(cell);
            BitSet cellRemovablePotentials = removablePotentials.get(cell);
    		toBeRemovedApplied.put(immutableCell, cellRemovablePotentials);
            cell.removePotentialValues(cellRemovablePotentials);
        }
		if (getCell(HintType.HINT) != null) {
			Cell cell = getCell(HintType.HINT); 
            cell.setValueAndCancel(getValue(), cell.isGiven());
		}
		return theGrid;
    }

}